#include "usart.h"
#include "io.h"
#include "../lib/inc/stm32f10x_conf.h"
#include "../utils/ringbuf.h"

Ringbuf_StaticDefine(uint8_t, usart1_buf, 100);
Ringbuf_StaticDefine(uint8_t, usart2_buf, 100);
Ringbuf_StaticDefine(uint8_t, usart3_buf, 100);
Ringbuf_StaticDefine(uint8_t, usart4_buf, 20);
Ringbuf_StaticDefine(uint8_t, usart5_buf, 20);

const USART_TypeDef* usarts[] = {
    USART1, USART2, USART3,
    UART4, UART5
};

const uint8_t usarts_irqn[] = {
    USART1_IRQn, USART2_IRQn, USART3_IRQn,
    UART4_IRQn, UART5_IRQn
};

const uint32_t rcc_usarts[] = {
    RCC_APB2Periph_USART1,
    RCC_APB1Periph_USART2,
    RCC_APB1Periph_USART3,
    RCC_APB1Periph_UART4,
    RCC_APB1Periph_UART5
};

typedef struct{
    uint8_t tx;
    uint8_t rx;
}USART_PIN_T;

const USART_PIN_T usarts_pin[][3] = {
    {{PA9,  PA10}, {PB6,  PB7 }, {PB6, PB7}},   // USART1
    {{PA2,  PA3 }, {PD5,  PD6 }, {PD5, PD6}},   // USART2
    {{PB10, PB11}, {PC10, PC11}, {PD8, PD9}},   // USART3
    {{PC10, PC11}, {PC10, PC11}, {PC10, PC11}}, // UART4
    {{PC12, PD2 }, {PC12, PD2 }, {PC12, PD2}},  // UART5
};

void UsartGpioInit(uint8_t usart, uint8_t remap) {
    uint8_t tx = usarts_pin[usart][remap].tx;
    uint8_t rx = usarts_pin[usart][remap].rx;

    switch (usart) {
        case USART_1:
            if (remap) GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE);
            break;
        case USART_2:
            if (remap) GPIO_PinRemapConfig(GPIO_Remap_USART2, ENABLE);
            break;
        case USART_3:
            if (remap == 1) GPIO_PinRemapConfig(GPIO_PartialRemap_USART3, ENABLE);
            else if (remap == 2) GPIO_PinRemapConfig(GPIO_FullRemap_USART3, ENABLE);
            break;
    }
    IOInit(tx, Mode_AF_PP);
    IOInit(rx, Mode_IN_FLOATING);
}

void UsartInit(uint8_t usart, uint32_t baudrate, uint8_t remap) {
    USART_InitTypeDef USART_InitStruct;
    USART_DeInit((USART_TypeDef*)usarts[usart]);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
    if (usart == USART_1) 
        RCC_APB2PeriphClockCmd(rcc_usarts[usart], ENABLE);
    else
        RCC_APB1PeriphClockCmd(rcc_usarts[usart], ENABLE);
    if (remap > 2) {
        while(1);
    }
    UsartGpioInit(usart, remap);

    USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
    USART_InitStruct.USART_Parity = USART_Parity_No;
    USART_InitStruct.USART_StopBits = USART_StopBits_1;
    USART_InitStruct.USART_WordLength = 8;
    USART_InitStruct.USART_BaudRate = baudrate;

    USART_Init((USART_TypeDef*)usarts[usart], &USART_InitStruct);
    USART_Cmd((USART_TypeDef*)usarts[usart], ENABLE);
    USART_ClearFlag((USART_TypeDef*)usarts[usart], USART_FLAG_TC);
    USART_ClearITPendingBit((USART_TypeDef*)usarts[usart], USART_IT_RXNE);
    USART_ITConfig((USART_TypeDef*)usarts[usart], USART_IT_RXNE, ENABLE);

    UsartNvicInit(usart, 1, 1);

}

void UsartNvicInit(uint8_t usart, uint8_t PreemptionPriority, uint8_t SubPriority) {
    NVIC_InitTypeDef NVicInit;
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    NVicInit.NVIC_IRQChannelPreemptionPriority = PreemptionPriority;
    NVicInit.NVIC_IRQChannel = usarts_irqn[usart];
    NVicInit.NVIC_IRQChannelSubPriority = SubPriority;
    NVicInit.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVicInit);
}

void UsartPutC(uint8_t usart, unsigned char c) {
    USART_SendData((USART_TypeDef*)usarts[usart], c);  
    while (USART_GetFlagStatus((USART_TypeDef*)usarts[usart], USART_FLAG_TXE) == RESET );
}

void UsartPuts(uint8_t usart, unsigned char *str,int num) {
	int i;
	for(i = 0; i < num; i++) {
		USART_SendData((USART_TypeDef*)usarts[usart], *str++);  
		while (USART_GetFlagStatus((USART_TypeDef*)usarts[usart], USART_FLAG_TXE) == RESET);
	}	
}

uint8_t UsartGetC(uint8_t usart, uint8_t *out) {
    switch (usart) {
        case USART_1:
            if (!Ringbuf_IsNull(usart1_buf)) {
                *out = Ringbuf_GetData(usart1_buf);
                Ringbuf_DelData(usart1_buf, 1);
                return U_ERR_SUCCSE;
            }
            break;
        case USART_2:
            if (!Ringbuf_IsNull(usart2_buf)) {
                *out = Ringbuf_GetData(usart2_buf);
                Ringbuf_DelData(usart2_buf, 1);
                return U_ERR_SUCCSE;
            }
            break;
        case USART_3:
            if (!Ringbuf_IsNull(usart3_buf)) {
                *out = Ringbuf_GetData(usart3_buf);
                Ringbuf_DelData(usart3_buf, 1);
                return U_ERR_SUCCSE;
            }
            break;
        case USART_4:
            if (!Ringbuf_IsNull(usart4_buf)) {
                *out = Ringbuf_GetData(usart4_buf);
                Ringbuf_DelData(usart4_buf, 1);
                return U_ERR_SUCCSE;
            }
            break;
        case USART_5:
            if (!Ringbuf_IsNull(usart5_buf)) {
                *out = Ringbuf_GetData(usart5_buf);
                Ringbuf_DelData(usart5_buf, 1);
                return U_ERR_SUCCSE;
            }
            break;
    }
    return U_ERR_NONE;
}

extern "C" {

void USART1_IRQHandler(void) {
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {
		uint8_t ch = USART_ReceiveData(USART1);
        if (!Ringbuf_IsFull(usart1_buf)) {
            Ringbuf_AddData(usart1_buf, ch);
        }
        USART_ClearFlag(USART1,USART_IT_RXNE);
	}
} 

void USART2_IRQHandler(void) {
	if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) {
		uint8_t ch = USART_ReceiveData(USART2);
        if (!Ringbuf_IsFull(usart2_buf)) {
            Ringbuf_AddData(usart2_buf, ch);
        }
        USART_ClearFlag(USART2,USART_IT_RXNE);
	}
}

void USART3_IRQHandler(void) {
	if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET) {
		uint8_t ch = USART_ReceiveData(USART1);
        if (!Ringbuf_IsFull(usart3_buf)) {
            Ringbuf_AddData(usart3_buf, ch);
        }
        USART_ClearFlag(USART3,USART_IT_RXNE);
	}
}

void UART4_IRQHandler(void) {
	if(USART_GetITStatus(UART4, USART_IT_RXNE) != RESET){
		uint8_t ch = USART_ReceiveData(USART1);
        if (!Ringbuf_IsFull(usart4_buf)) {
            Ringbuf_AddData(usart4_buf, ch);
        }
        USART_ClearFlag(UART4,USART_IT_RXNE);
	}
}

void UART5_IRQHandler(void) {
	if(USART_GetITStatus(UART5, USART_IT_RXNE) != RESET) {
		uint8_t ch = USART_ReceiveData(USART1);
        if (!Ringbuf_IsFull(usart5_buf)) {
            Ringbuf_AddData(usart5_buf, ch);
        }
        USART_ClearFlag(UART5,USART_IT_RXNE);
	}
}

}
